#pragma once

#include <iostream>
#include "sock.hpp"
#include "err.hpp"

namespace select_ns
{
    class SelectServer
    {
        static const int defaultport = 8080;
        static const int fdnum = sizeof(fd_set) * 8;
        static const int defaultfd = -1;

    public:
        SelectServer(int port = defaultport)
            : _port(port), _listensock(-1), fdarray(nullptr)
        {
        }

        void Print()
        {
            std::cout << "fd list: ";
            for (int i = 0; i < fdnum; ++i)
            {
                if (fdarray[i] != defaultfd)
                    std::cout << fdarray[i] << " ";
            }
            std::cout << std::endl;
        }

        void HandlerEvent(fd_set &rfds)
        {
            // 目前就一个listensock
            if (FD_ISSET(_listensock, &rfds))
            {
                // 链接就绪，可以读取数据了
                std::string clientip;
                uint16_t clientport = 0;
                int sock = Sock::Accept(_listensock, &clientip, &clientport); // 等 + 获取
                if (sock < 0)
                    return;
                logMessage(NORMAL, "accept success [%s:%d]", clientip.c_str(), clientport);

                // 在这里没有确定是否有资源，让select检测资源是否就绪
                // 将新的sock托管给select就是将sock添加到fdarray中
                int i = 0;
                for (; i < fdnum; ++i)
                {
                    if (fdarray[i] != defaultfd)
                        continue;
                    else
                        break;
                }
                if (i == fdnum)
                {
                    logMessage(WARNING, "server is full, please wait");
                    close(sock);
                }
                else
                {
                    fdarray[i] = sock;
                }
            }
        }

        void initServer()
        {
            _listensock = Sock::Socket();
            Sock::Bind(_listensock, _port);
            Sock::Listen(_listensock);
            // logMessage(NORMAL, "create socket success : %d", _listensock);
            fdarray = new int[fdnum];
            for (int i = 0; i < fdnum; ++i)
                fdarray[i] = defaultfd;
            fdarray[0] = _listensock;
        }

        void start()
        {
            for (;;)
            {
                fd_set rfds;
                FD_ZERO(&rfds); // 清除rfds内容
                int maxfd = fdarray[0];
                for (int i = 0; i < fdnum; ++i)
                {
                    if (fdarray[i] == defaultfd)
                        continue;
                    FD_SET(fdarray[i], &rfds); // 合法fd添加到读文件描述符集中

                    if (maxfd < fdarray[i])
                    {
                        maxfd = fdarray[i]; // 更新最大的fd
                    }
                }
                // 多路转接
                // struct timeval timeout = {1,0};
                // int n = select(_listensock + 1, &rfds, nullptr, nullptr, &timeout); // 每次循环后，如果没有就会减小阻塞等待的时间
                // 一般用select需要程序员维护一个有效的fd数组
                int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);
                switch (n)
                {
                case 0:
                    logMessage(NORMAL, "timeout... ");
                    break;
                case -1:
                    logMessage(WARNING, "select error, code: %d, err string: %s", errno, strerror(errno));
                default:
                    // 说明有事件就绪了
                    logMessage(NORMAL, "accept a new link success, get new sock: %d", sock); // 没取走socket，一直在就绪
                    HandlerEvent(&rfds);
                    break;
                }
                // 阻塞等待
                // std::string clientip;
                // uint16_t clientport = 0;
                // int sock = Sock::Accept(_listensock, &clientip, &clientport);   // 等 + 获取
                // if (sock < 0)
                //     continue;

                // 服务器开始业务
            }
        }

        ~SelectServer()
        {
            if (_listensock < 0)
                close(_listensock);
            if (fdarray)
                delete[] fdarray;
        }

    private:
        int _port;
        int _listensock;
        int *fdarray;
    };

} // namespace select_ns
